//Debug Class //
{
function Debug(str)
{
	if(typeof(str) != 'undefined'){
	this.bug = document.getElementById(str);
	}
	else{
	this.bug = document.getElementById(str);
	}
}

Debug.prototype.println = function()
{
	for(var i=0; i<arguments.length; i++)
	{
		this.print(arguments[i]);
	}
	this.bug.innerHTML += "<br/>\n";
}

Debug.prototype.print = function()
{
	for(var i=0; i<arguments.length; i++)
	{
		this.bug.innerHTML += arguments[i]+' ';
	}
}

Debug.prototype.printprop = function(key,value)
{
	this.println("<em>"+key+":</em>",value);
}

Debug.prototype.printobj = function(obj)
{
	this.println(obj);
	for(var key in obj)
	{
		if(obj[key])this.printprop(key,obj[key]);
	}
}

Debug.prototype.clear = function()
{
	this.bug.innerHTML = "";
}
}

var bug;
bug = new Debug('debug')





function initField()
{
	bug = new Debug('debug');
	var canvas = SVG(document.getElementById('canvas'));
	yxstrs = [document.getElementById('dx').value,document.getElementById('dy').value]
	var vf = new VectorField(canvas,yxstrs[0], yxstrs[1], {'x_min':-3,'x_max':3,'y_min':-3,'y_max':3});
	var plot = document.getElementById('redraw');
	
	plot.onclick = function(evt) 
	{
		vf.removeVectors();
		vf.drawVectors(document.getElementById('dx').value,document.getElementById('dy').value);
	}
	
	
}

function init()
{
	bug = new Debug('debug');
	var canvas = SVG(document.getElementById('canvas'));
	var graphProperties = {
		'graph-type':document.getElementById('gtype').options[document.getElementById('gtype').selectedIndex].value,
		'background-color':'lavenderblush',
		'function-color':document.getElementById('gcolor').options[document.getElementById('gcolor').selectedIndex].value,
		'Nticks':10,
		'ranges':
		{
			'x_min':parseFloat(document.getElementById('xmin').value),
			'x_max':parseFloat(document.getElementById('xmax').value),
			'y_min':parseFloat(document.getElementById('ymin').value),
			'y_max':parseFloat(document.getElementById('ymax').value)
		},
		'oneDimProperties':
		{
			'ystr':document.getElementById('y').value
		},
		'fieldProperties':
		{
			'xstr':document.getElementById('dx').value,
			'ystr':document.getElementById('dy').value
		},
		
	}
	
	 xyplotProperties =
		{
			'getPoints': function()
			{
				dtable = document.getElementById('datatable');
				Npoints = dtable.childNodes.length-4
				
				dpoints={}
				j=0
				xs=[]
				ys=[]
				for (var i=0;i<Npoints;i++)
				{
					if (document.getElementById('x'+i).value)
					{
						xs[j] = parseFloat(document.getElementById('x'+i).value);
						ys[j] = parseFloat(document.getElementById('y'+i).value);
						j++
					}
				}
				
				return {'xs':xs,'ys':ys}
			}
		}
	graphProperties.xyplotProperties = xyplotProperties
	switch(graphProperties['graph-type'])
	{
		case 'vectors':
			document.getElementById('onedim_input').style.display='none';
			document.getElementById('xyplot_input').style.display='none';
			document.getElementById('xyplot_input2').style.display='none';
			document.getElementById('fieldinput').style.display='inline';
			svgEle = new Graph(canvas, graphProperties)
			funct = new VectorField(svgEle)
		break;
		case 'onedim':
			document.getElementById('onedim_input').style.display='inline';
			document.getElementById('xyplot_input').style.display='none';
			document.getElementById('xyplot_input2').style.display='none';
			document.getElementById('fieldinput').style.display='none';
			svgEle = new Graph(canvas,graphProperties)
			funct = new OneDim(svgEle)
			
		break;
		case 'xyplot':
			document.getElementById('onedim_input').style.display='none';
			document.getElementById('xyplot_input').style.display='inline';
			document.getElementById('xyplot_input2').style.display='inline';
			document.getElementById('fieldinput').style.display='none';
			svgEle = new Graph(canvas,graphProperties);
			funct = new Plot(svgEle);
		break;
		
		case 'test':
			graph = new Graph(canvas, graphProperties);
			graph.poo()
			cir = makeCircle()
			graph.drawAxis()
		break;
		default:
			
			
	}
	
	
	
}

function Graph(svgEle,graphProperties)
{
	
	
	svgEle.background = new SVG('rect',{'x':0,'y':0,'width':'100%','height':'90%','fill':graphProperties['background-color']});
	
	svgEle.graphProperties = graphProperties
	
	
	
	svgEle.axis = {}
	svgEle.axis.x_min = graphProperties.ranges.x_min || -11.5;
	svgEle.axis.y_min = graphProperties.ranges.y_min || -3;
	svgEle.axis.x_max = graphProperties.ranges.x_max || 3;
	svgEle.axis.y_max = graphProperties.ranges.y_max || 6.7;
	
	
	
	svgEle.appendChild(svgEle.background);
	svgEle.drawAxis = this.drawAxis;
	
	
	svgEle.graph = svgEle.drawAxis();
	svgEle.appendChild(svgEle.graph)
	return svgEle
}

Graph.prototype.drawAxis = function()
{
	
	var clientBB = this.background.getBoundingClientRect();
	
	
	x_dim = (this.axis.x_max-this.axis.x_min);
	y_dim = (this.axis.y_max-this.axis.y_min);
	
	
	
	cx = this.axis.x_min+x_dim/2
	cy = this.axis.y_min+y_dim/2
	//create transform group so x and y coordients map to svg element
	scaleX = clientBB.width/x_dim;
	scaleY = -clientBB.height/y_dim;
	
	var transX = (clientBB.width/2-cx*scaleX);
	
	var transY = (clientBB.height/2-cy*scaleY);
	
	var b = 0; //boarder in screen pixels
	// make axis
	
	this.axis.x_axis = new SVG('line',{x1:b,x2:clientBB.width-b,y1:clientBB.height/2-b,y2:clientBB.height/2-b,'stroke-width':1/5,'stroke':'black',id:'x_axis'});
	this.axis.y_axis = new SVG('line',{x1:b+clientBB.width/2,x2:b+clientBB.width/2,y1:b,y2:clientBB.height-b,'stroke-width':1/5,'stroke':'black',id:'y_axis'});
	
	// add axes
	this.appendChild(this.axis.x_axis); this.appendChild(this.axis.y_axis);
	
	//make and add tick marks
	Nticks = this.graphProperties.Nticks; 
	
	xval=this.axis.x_min
	for(var xcount=0;xcount<=clientBB.width;xcount+=clientBB.width/Nticks)
	{
		this.appendChild(new SVG('line',{x1:xcount, y1:clientBB.height/2+5, x2:xcount, y2:clientBB.height/2-5, 'stroke-width':1,'stroke':'black'}))
		holder = new SVG('text',{x:xcount-5, y:clientBB.height/2+20, 'font-size':8})
		holder.textContent=Math.round(xval*100)/100
		xval+=x_dim/Nticks
		this.appendChild(holder)
	}
	
	yval=this.axis.y_max
	for(var ycount=0;ycount<=clientBB.height;ycount+=clientBB.height/Nticks)
	{
		this.appendChild(new SVG('line',{y1:ycount, x1:clientBB.width/2+5, y2:ycount, x2:clientBB.width/2-5, 'stroke-width':1,'stroke':'black'}))
		holder = new SVG('text',{x:clientBB.width/2+5, y:ycount+5, 'font-size':8})
		holder.textContent=Math.round(yval*100)/100
		
		yval-=y_dim/Nticks
		this.appendChild(holder)
	}
	
	
	var transform = "matrix("+scaleX+",0,0,"+scaleY+","+transX+","+transY+")";
	this.graph = new SVG('g',{"transform":transform});
	
	this.graph.x_dim = x_dim
	this.graph.y_dim = y_dim
	
	
	return this.graph
	

}


Graph.prototype.addFunction = function(funct)
{
	for (var i=0;i<funct.length;i++)
	{
		this.graph.appendChild(funct[i])
	}
}

function makeCircle()
{
	return [new SVG('circle',{cx:0,cy:0,r:1})]
}

function Plot(svgEle,graphProperties)
{
	svgEle.drawPlot = this.drawPlot;
	svgEle.drawPlot()
	
	return svgEle;
}

Plot.prototype.drawPlot = function()
{
	coords = this.graphProperties.xyplotProperties.getPoints();
	xs = coords.xs
	ys = coords.ys
	
	item_list = []
	i=0
	
	radius = Math.sqrt(this.graph.x_dim*this.graph.x_dim+this.graph.y_dim*this.graph.y_dim)/600
	for(var point=0;point<xs.length;point++)
	{
		item_list[i] = (new SVG('circle',{cx:xs[point], cy:ys[point], r:radius, fill:'red'}))
		this.graph.appendChild(item_list[i])
		i++
		
		
	}
	
	var model = linearFit(xs,ys);
	return item_list
	
	
	
}
function OneDim(svgEle) 
{

	svgEle.drawFunction = this.drawFunction;
	svgEle.erase = this.erase
	
	svgEle.drawFunction(svgEle.graphProperties.oneDimProperties.ystr)
	
	return svgEle;
}

OneDim.prototype.drawFunction = function(y_str) 
{
	stroke_width = Math.sqrt(this.graph.x_dim*this.graph.x_dim+this.graph.y_dim*this.graph.y_dim)/500; //function stroke width 
	Ntests = 100; // number of independent variable test points (the higher this number the smother the graph)
	
	funct = new SVG('path',{'fill':'none', 'stroke':this.graphProperties['function-color'], 'stroke-width':stroke_width, id:'funct'})
	
	
	
	
	for(x=this.axis.x_min; x<=this.axis.x_max; x+=this.graph.x_dim/Ntests)
	
	{
		y=Math.floor(eval(y_str.replace('x',x))*1000)/1000
		if (x==this.axis.x_min) 
		{
			d = "M"+String(x)+" "+String(y);			
		}
		else if(this.axis.x_min <= y && this.axis.x_max >= y)
		{
			d+=" L"+String(x)+" "+String(y);
		}
		
		
	}
	
	funct.setAttributes({'d':d})
	
	this.graph.appendChild(funct)
}

OneDim.prototype.erase = function()
{
	while(this.graph.hasChildNodes())
	{
		this.graph.removeChild(this.graph.firstChild);
	}
}

function VectorField(svgEle)
{
	
      
       
       svgEle.drawVectors = this.drawVectors;
       svgEle.drawVectors();
       return svgEle;
}



VectorField.prototype.screenXYtoSVG = function(x,y)
{
        screenCTM = this.graph.getScreenCTM().inverse();
        newCTM = screenCTM.multiply({a:0,b:0,c:0,d:0,e:400,f:400});
        return {x:newCTM.e,y:newCTM.f}
}

VectorField.prototype.drawVectors = function()
{
        
        x_str = this.graphProperties.fieldProperties.xstr;
        y_str = this.graphProperties.fieldProperties.ystr;
        for(x=this.axis.x_min; x<=this.axis.x_max; x+=.5)
        {
                for(y=this.axis.y_min; y<=this.axis.y_max; y+=.5)
                {
                        fx = eval(x_str.replace('x',x).replace('y',y));
                        fy = eval(y_str.replace('x',x).replace('y',y));
                        
                        var v = new Vector(x,y,fx,fy,this.graphProperties);
                        
                        for(vecpart in v.vector) {
                                
                                this.graph.appendChild(v.vector[vecpart]);
                        }
                        
                }
        }
}


VectorField.prototype.removeVectors = function()
{
        while(this.graph.hasChildNodes())
        {
                this.graph.removeChild(this.graph.firstChild);
        }
}

function Vector(x,y,dx,dy,graphProperties)
{
        angle = findAngle(dx,dy)
        Length = Math.sqrt(Math.pow(dx,2)+Math.pow(dy,2))/4
        pos = [x+dx,y+dy]
        this.graphProperties = graphProperties
        ear1_ends = set_xy(pos, angle+35-180, Math.sqrt(Math.pow(dx,2)+Math.pow(dy,2))/5)
        ear2_ends = set_xy(pos, angle-35-180, Math.sqrt(Math.pow(dx,2)+Math.pow(dy,2))/5)
        
        
        this.vector = {
                'body':new SVG('line',{'x1':x,'x2':x+dx,'y1':y,'y2':y+dy,'stroke':this.graphProperties['function-color'],'stroke-width':1/100}), 
                'ear1':new SVG('line',{'x1':x+dx,'x2':ear1_ends[0],'y1':y+dy,'y2':ear1_ends[1],'stroke':this.graphProperties['function-color'],'stroke-width':1/100}),
                'ear2':new SVG('line',{'x1':x+dx,'x2':ear2_ends[0],'y1':y+dy,'y2':ear2_ends[1],'stroke':this.graphProperties['function-color'],'stroke-width':1/100})
        };
        

}

Vector.prototype.draw = function(x,y,dx,dy)
{
        this.vector.setAttribute('x1',x);
        this.vector.setAttribute('x2',x+dx);
        this.vector.setAttribute('y1',y);
        this.vector.setAttribute('y2',y+dy);
}

function SVG(nodeName,attributes)
	{
		
		var that = (typeof(nodeName)=='string')?document.createElementNS('http://www.w3.org/2000/svg',nodeName):nodeName;
		for(key in SVG.prototype)
		{
			try{
			that[key] = SVG.prototype[key]
			}
			catch (e){}
		}
		that.setAttributes(attributes);
		return that
	}

SVG.prototype.setAttributes = function(attributes)
	{
		for(var attr in attributes)
		{
			this.setAttribute(attr,attributes[attr]);
		}
	}
	





function findAngle(x,y) {
	if (x != 0) {
		angle = Math.atan(y/parseFloat(x))*180/Math.PI
		
		if (x < 0) {
			angle = angle + 180
		}
		else{
			if (y < 0){
				angle = 360 + angle
			}
		}
	}
	else{
		if (y < 0) {
			angle = 270
		}
		else{
				angle = 90
		}
		if(y == 0) {
			if (x < 0){
				angle = 180	
			}
		}
	}
	return angle
}

function set_xy(pos, theta, Length) {//determine cartesian coordinates given a length and angle (polar cordinates)
	
	x_coord = Length*Math.cos(theta*Math.PI/180)+pos[0]
	y_coord = Length*Math.sin(theta*Math.PI/180)+pos[1]
	

	return [x_coord,y_coord]	
}

function catchMenuType(menuitem) 
{
	switch(menuitem)
	{
		case 'vectors':
			document.getElementById('onedim_input').style.display='none';
			document.getElementById('fieldinput').style.display='inline';
			initField();
		break;
		case 'onedim':
			document.getElementById('onedim_input').style.display='inline';
			document.getElementById('fieldinput').style.display='none';
			initOneDim();
		break;
		default:
			document.getElementById('onedim_input').style.display='inline';
			document.getElementById('fieldinput').style.display='none';
			initOneDim();
			
	}
	
}

function catchMenuColor(color)
{
	graphProperties['plot-color']=color
	
	gtypes=document.getElementById('gtype').options
	
	catchMenuType(gtypes[gtypes.selectedIndex].value)
}

function convertCoords(x, scaleX, transX)
{
	return (x+transX)*scaleX
}
function displayPlotInput()
{
	input=document.getElementById('xyplot_input2')
	if(input.style.display == 'none')
	{
		input.style.display = 'inline';
	}
	else
	{
		input.style.display = 'none';
	}
	init()
}



function addDataPoint()
{
	table = document.getElementById('datatable');
	
	
	row = document.createElement('tr');
	Npoint=table.childNodes.length-4
	
	td1 = document.createElement('td');
	in1 = document.createElement('input');
	in1.setAttribute('size','5');
	in1.setAttribute('id','x'+String(Npoint));
	
	td1.appendChild(in1);
	
	td2 = document.createElement('td');
	in2 = document.createElement('input');
	in2.setAttribute('size','5');
	in2.setAttribute('id','y'+String(Npoint));
	
	td2.appendChild(in2);
	
	row.appendChild(td1); row.appendChild(td2);
	table.appendChild(row)
	
}

function linearFit(xs,ys)
{
	sumX = 0;
	sumX2 = 0;
	sumY = 0;
	sumXY = 0
	N = xs.length;	
	for(var i=0;i<xs.length;i++)
	{
		sumX+= xs[i]
		sumX2+= xs[i]*xs[i]
		sumY+= ys[i]
		sumXY+= xs[i]*ys[i]
	}
	slope=(N*sumXY-sumX*sumY)/(N*sumX2-sumX*sumX)
	yintercept=(sumY-slope*sumX)/N
	return {'slope':slope, 'y-intercept':yintercept}	
}